# the project class is the central class in this application
# everything revolves around a project. 
class Project < ActiveRecord::Base
  include UserInfo
  validates_presence_of :name
  validates_uniqueness_of :short_name
  validates_numericality_of :projection_weeks
  validates_presence_of :site_update_frequency
  validates_presence_of :bug_tool_url
  has_many :generations
  has_many :owners
  has_many :bug_lists
  has_many :administrators
  has_many :admins, :through => :administrators, :source => :person
  has_many :teams
  belongs_to :bug_tool_url
  belongs_to :site_update_frequency

  require 'date'
  require 'rubygems'
	require 'active_support'
	
	def new
	  @project = super
	  @project.projection_start_date = today
  end
  
  # test method
  def respond_already
     publish :the_ear, "this is a project"
  end
    
  ##  
  # determines if the site requres further setup 
  # before it can be generated  
   def setup_required?
     if !has_teams? || !has_bug_lists?
       true
     else
       false
     end
   end  
    
   ##
   # determines if the project has any teams  
   def has_teams?
     if self.teams.count == 0
       false
     else
       true
     end
   end   
 
   ##
   # determines if the project has any bug lists
   def has_bug_lists?
     if self.bug_lists.count == 0
       false
     else
       true
     end
   end
 
  
   ##
   # only authenticated users are authorized to create records
    def authorized_for_update?
      self.is_logged_in_project_administrator?
    end

    ##
    # determines if the currently logged in user is an admin for this site
    def is_logged_in_project_administrator?
      return true
      logger.info "checking admin.usernames"
      logger.info "project name" 
      self.admins.each do |admin|
        logger.info "checking admin.username: " + admin.username
        if admin.username == current_user
          return true
        end
      end
      logger.info "no match for admin.usernames"
      # this is not the username of this project's admin
      return false     
    end


  ## 
  # returns a string of the administrators full names.
  # this is used in constructing generator configuration files
  def administrator_full_names
    admin_list = ''
    
    self.administrators.each do |admin| 
      if !admin.person.first_name.nil? 
        admin_list += admin.person.first_name + ' '
      end
      if !admin.person.last_name.nil? 
        admin_list += admin.person.last_name + ' '
      end
      admin_list += '('+admin.person.username+')  '
    end
    admin_list
  end

  ##
  # returns a string of the administrators email addresses.
  # this is used in constructing generator configuration files
  def administrator_email_addresses
    emails = ''
    self.administrators.each do |admin|   
      emails += admin.person.username + ' ' 
    end
    emails
  end
  
  ##
  # returns the projected end date 
  def projection_end_date
    projection_weeks.weeks.since(projection_start_date)
  end
  
  ##
  # return an array of all of the projection dates
  # starting with the project start date and going
  # out as far as the the projection weeks
  def projection_dates
    dates_array = Array.new
    projection_weeks.times do|week_number|
      dates_array.push(week_number.weeks.since(projection_start_date))      
    end
    
    return dates_array
  end
  
  ##
  # returns the last complete generation
  def latest_complete_generation
    Generation.find(:first,  :conditions => {:project_id => self.id, :complete => true }, :order => 'end_time DESC' ) 
  end
  
  ##
  #  returns the time of the latest complete generation
  def latest_complete_generation_time
    generation  = latest_complete_generation
    unless generation.nil?
      return generation.start_time
    end
    ## there no generation so we can't find a time
    return Time.now - 2.years
  end
  
  ##
  # Retruns true if the site is currently being generated
  def generating?
    logger.info "GENERATING?"
    last_gen = self.latest_generation
    if !last_gen.nil?
      logger.info "latest_generation.complete" + last_gen.complete.to_s
      if last_gen.complete
        ## we are not currently generating this site
        return false
      else
        #we are currently generating this site
        return true
      end
    else
      logger.info "the latest generation was nil"
      return false
    end
  end
  
  ##
  # returns the latest generation. 
  # Warning: this may be a generation in progress
  def latest_generation
    latest_generation = Generation.find(:first,  :conditions => {:project_id => self.id }, :order => 'start_time DESC' ) 
  end
  
  ##
  # returns true if the project should be generated
  def should_generate?
    
    if !active 
      ## never generate if the site is disabled or currently generating
      return false
    end
    
    if setup_required?
      ## this site might be active, but it requres setup, so don't generate it.
      return false
    end
    
    if generating?
      ## don;t generate a site that's already generating
      return false
    end
    
    unless !latest_complete_generation_time.nil?
      ## if it never generated then it should
      return true
    end
    
    if site_update_frequency.generator_name.eql?('weekly')  
       # FIXME: this will result in some minor site creep
       # we need to deal with days and not datetime here
       logger.info "last complete generation time: " + latest_complete_generation_time.to_s
       if latest_complete_generation_time < Time.now - 1.week
         return true
       end
    elsif site_update_frequency.generator_name.eql?('daily')  
      # FIXME: this will result in some minor site generation time creep
      # we need to deal with days and not datetime here
      logger.info "TESTING DAILY GENERATION TIMES"
      logger.info "last completed daily generation time: " + latest_complete_generation_time.to_s
      #logger.info "Time.now - 1.day " + Time.now - 1.day
      if latest_complete_generation_time < Time.now - 1.day
        logger.info "Yes Generate " + name
         return true
       end
    else
      ## constantly sites should always generate
      return true
    end
    
    return false;
    
  end
  
  ##
  # get the columns as know by the generator in a comma seperated list
  def table_columns_order_as_csv
    ## bug_columns_helper
    BugColumnsHelper.format_columns_for_generator(self.table_column_order)    
  end

  ##
  # returns the full path to the site director for this project
  def site_directory
   SITES_DIRECTORY + self.short_name + "/"
  end

  def site_config_directory
    site_directory + 'configuration/'
  end
  
  def site_output_directory
    site_directory + 'output/'
  end
  
  def site_history_directory
    site_output_directory + 'history/'
  end
  
  def site_owners_directory
    site_output_directory + 'owner_lists/'
  end
  
  def site_projections_directory
    site_output_directory + 'projections/'
  end
  
  def site_history_url
    SITES_URL + self.short_name + "/output/history/"
  end

  def site_website_url
    SITES_URL + self.short_name + "/output/website/"
  end
  
  ##
  # returns a stirng containing the site properties in the XML 
  # format know by the controller
  def site_properties_as_xml

    output = %{<site-properties>

    <!--

        ########################################################################
        ##                                                                    ##
        ##                                                                    ##
        ##                        WARNING WARNING WARNING                     ##
        ##                                                                    ##
        ##      Do not manyally edit this file! This configuration file can   ##
        ##      only be updated through the site Administration web           ##  
        ##      interface which you can access by clicking on "Site           ##
        ##      Administration" from the product page of your site.           ##
        ##                                                                    ##      
        ##      Any attempt to edit this file manually will result in data    ##
        ##      loss and this enotify site failing to run in the future.      ##
        ##                                                                    ##
        ##                                                                    ##
        ########################################################################

    -->
    }

    output += '<property name="site_active" value="' + self.active.to_s + '" type="boolean" />' + "\n"
    output += '<property name="site_update_frequency" value="' + self.site_update_frequency.generator_name.to_s + '"  />'  + "\n"
    output += '<property name="project_name" value="' + CGI.escapeHTML(self.name) + '"  />'  + "\n"
    output += '<property name="projection_start_date" value="' + self.projection_start_date.to_s + '" type="time"  />'   + "\n"
    output += '<property name="projection_status_model_name" value=""  />'  + "\n"
    output += '<property name="number_of_projection_weeks" value="' + self.projection_weeks.to_s + '" />'  + "\n"
    output += '<property name="administrator_full_name" value="' 
    self.administrators.each do |admin|
      output += admin.person.full_name_and_username
      unless admin == administrators.last
        output += ', '
      end
    end
    output += '"   />'  + "\n"
    output += '<property name="administrator_email_address" value="' + self.administrator_email_addresses + '"  />'  + "\n"
    output += '<property name="bug_tool_url" value="' + self.bug_tool_url.url + '"  />'  + "\n"
    output += '<property name="bug_table_column_order" value="' + self.table_columns_order_as_csv + '" type="csv"   />'   + "\n"
    output += '<property name="base_url"   value="/"     />'   + "\n"

    # this is left out because sortable isn't going to be an option in the new generator  
    # output += '<property name="site_columns_sortable"  value="'+project.empty+'"     />'

    output += '</site-properties>'
    output
  end

  ##
  # returns a string containing the projects bug lists in the XML 
  # format known to the controller
  def bug_lists_as_xml

    output = %{<qddts-queries>

    <!--

        ########################################################################
        ##                                                                    ##
        ##                                                                    ##
        ##                        WARNING WARNING WARNING                     ##
        ##                                                                    ##
        ##      Do not manyally edit this file! This configuration file can   ##
        ##      only be updated through the site Administration web           ##  
        ##      interface which you can access by clicking on "Site           ##
        ##      Administration" from the product page of your site.           ##
        ##                                                                    ##      
        ##      Any attempt to edit this file manually will result in data    ##
        ##      loss and this enotify site failing to run in the future.      ##
        ##                                                                    ##
        ##                                                                    ##
        ########################################################################



    -->
    }

    self.bug_lists.each do |list|
      output += "<query-def \n"
      output += "name=\"" + CGI.escapeHTML(list.name) + "\"\n"
      output += "use_projections=\"" + list.use_projections.to_s + "\"\n"
      output += "use_ownership_change=\"" + list.use_ownership_change.to_s + "\"\n"
      output += "is_verification_list=\"" + list.is_verification_list.to_s + "\"\n"
      output += "ownership_model_name=\"" + list.ownership_model.legacy_name.to_s + "\"\n"
      output += "scope_filter=\"individual\">\n"    

    	output += '<display-name color="' + list.display_name_color + '">' + CGI.escapeHTML(list.display_name) + '</display-name>' + "\n" 
    	output += '<description>' + CGI.escapeHTML(list.description) +'</description>' + "\n" 
      output += '<expert-query>' + CGI.escapeHTML(list.expert_query) +'</expert-query>' + "\n" 
      output += '<query_bug_table_column_order override="' + list.override_column_order.to_s + '" value="' + list.table_columns_order_as_csv + '" />'
      output += "</query-def> \n"

    end

    output += "</qddts-queries> \n"
    output
  end

  ##
  # publishes the site properties XML file to the 
  # site directory for this project
  def publish_site_properties
    publish_config_file('site_properties.xml', site_properties_as_xml)
  end

  ##
  # publishes the bug list in query.xml format to 
  # the site directory for this project
  def publish_bug_lists
    publish_config_file('queries.xml', bug_lists_as_xml)
  end 

  ##
  # publishes the owner lists to the site owners directry.
  def publish_owner_lists
    self.teams.each do |team|
      f = File.open(site_owners_directory + team.manager.username + ".txt","w")
      file_content = ''
      team.team_members.each do |team_member|
        file_content += team_member.as_csv + "\n"
      end 
      f.puts file_content
      f.close
    end
  end

  ##
  # writes file_contents to file name in the config 
  # directory for this project site.
  def publish_config_file(filename, file_content)
    ## make sure the site direcrtory exists 
    f = File.open(site_config_directory + filename,"w")
    f.puts file_content
    f.close
  end
  
  ##
  # publishes the projections for each team and 
  # for the overll project
  def publish_projections
    logger.info "publishing projections"
    teams.each do |team|
      team_projection_path = site_projections_directory + team.manager.username + '_team/'
      FileUtils.mkdir_p(team_projection_path)
      logger.info "publishing for team " + team.manager.full_name
      bug_lists.each do |bug_list|
        if bug_list.use_projections
            logger.info "writing file " + team_projection_path + bug_list.name + ".txt"
      
            ## write out the legacy generator projection files
            f = File.open(team_projection_path + bug_list.name + ".txt","w")
            file_content = ''
            Projection.find_projections_by_team_and_bug_list(team,bug_list).each do |projection|
              file_content += projection.as_generator_csv + "\n"
              f.puts file_content
            end
            f.close
        end
      end 
      
    end
  end
  
   
  ## 
  # creates all the site directories needed 
  # by this project, or it's generator
  def create_site_directory_structure
    FileUtils.mkdir_p([site_directory, 
                       site_config_directory, 
                       site_output_directory, 
                       site_history_directory,
                       site_owners_directory,
                       site_projections_directory,
                       site_output_directory + 'logs/',
                       site_output_directory + 'query_results/',
                       site_output_directory + 'site_records',
                       site_output_directory + 'website'] )
  end

  ##
  # builds the directory structure and puts 
  # all the confi files into it.
  def publish_site
    create_site_directory_structure
    publish_site_properties
    publish_bug_lists
    publish_owner_lists
    publish_projections
  end
  


end          
